﻿//========================================================= 
/**@file LC_EncryptCmdAdapter.h
 * @brief LC加密通信模式
 * 
 * @date 2017-10-13   14:31:07
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_LC_ENCRYPTCMDADAPTER_H_
#define _LIBZHOUYB_LC_ENCRYPTCMDADAPTER_H_
//--------------------------------------------------------- 
#include "../../../include/Extension.h"

#include "../../../extension/security/security.h"
using zhou_yb::extension::security::ISecurityAlgorithm;
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace lc {
//--------------------------------------------------------- 
/**
 * @brief 带通信加密功能的指令适配器
 * @date 2017-10-13 14:40
 */
class LC_EncryptCmdAdapter :
    public IInteractiveTrans,
    public DevAdapterBehavior<IInteractiveTrans>,
    public RefObject
{
public:
    /// 密钥器类型
    typedef MapArguments<byte, Ref<ISecurityAlgorithm> > CipherType;
    /// 密钥器枚举器类型
    typedef MapArguments<byte, Ref<ISecurityAlgorithm> >::EnumeratorType EnumeratorType;
    /// 按照格式接收数据
    static bool RecvByFormat(IInteractiveTrans& dev, ByteBuilder& emptyBuff, ByteBuilder* pCacheBuff = NULL)
    {
        const byte ACK_STX = 0x02;
        const size_t LEN_BYTE = 4;
        // 3 : 02 + MODE + LRC_LEN + (DATA + LRC + 03)
        const size_t CMD_HEAD_LEN = LEN_BYTE + 3;

        if(IInteractiveTransHelper::ReadAssert(dev, CMD_HEAD_LEN, emptyBuff) != DevHelper::EnumSUCCESS)
            return false;
        if(emptyBuff[0] != ACK_STX)
            return false;
        size_t lrcLen = emptyBuff[2];
        size_t len = 0;
        ByteConvert::ToObject<size_t>(emptyBuff.SubArray(3, LEN_BYTE), len, LEN_BYTE);
        len += CMD_HEAD_LEN;
        // LRC
        len += lrcLen;
        // 03
        len += 1;
        if(IInteractiveTransHelper::ReadAssert(dev, len, emptyBuff) != DevHelper::EnumSUCCESS)
            return false;
        size_t fulllen = emptyBuff.GetLength();
        if(fulllen > len)
        {
            if(pCacheBuff != NULL)
            {
                pCacheBuff->Append(emptyBuff.SubArray(len));
            }
            emptyBuff.RemoveTail(fulllen - len);
        }
        return true;
    }
    /// 返回指定的数据是否符合包长度
    static bool IsFormatted(const ByteArray& data, size_t* pCachedLength = NULL)
    {
        const byte ACK_STX = 0x02;
        const byte ACK_ETX = 0x03;

        const size_t LEN_BYTE = 4;
        // 3 : 02 + MODE + LRC_LEN + (DATA + LRC + 03)
        const size_t CMD_HEAD_LEN = LEN_BYTE + 3;
        ASSERT_Func(data.GetLength() >= CMD_HEAD_LEN);
        ASSERT_Func(data[0] == ACK_STX);
        size_t lrcLen = data[2];
        size_t len = 0;
        ByteConvert::ToObject<size_t>(data.SubArray(3, LEN_BYTE), len, LEN_BYTE);
        len += CMD_HEAD_LEN;
        // LRC
        len += lrcLen;
        // 03
        len += 1;
        ASSERT_Func(data.GetLength() >= len);
        ASSERT_Func(data[len - 1] == ACK_ETX);
        if(pCachedLength != NULL)
        {
            (*pCachedLength) = data.GetLength() - len;
        }
        return true;
    }
protected:
    /// 当前接收数据识别出来的格式
    byte _rMode;
    /// 发送数据的缓冲区
    ByteBuilder _sendBuff;
    /// 接收数据的缓冲区
    ByteBuilder _recvBuff;
    /// 接收ACK时返回上来的有效缓存数据
    ByteBuilder _cacheBuff;
    /// 获取明文数据的LRC
    bool _LRC(byte mode, const ByteArray& data, ByteBuilder& kcv, byte kcvLen)
    {
        // 不需要计算
        if(kcvLen < 1)
            return true;

        ByteBuilder buff(16);
        buff += mode;
        buff += kcvLen;
        ByteConvert::FromObject<size_t>(data.GetLength(), buff, 4);
        buff += data.SubArray(0, 10);
        ByteConvert::Fill(buff, 16, true, 0x00);

        ByteBuilder tmp(8);
        bool bOK = _Encrypt(mode, buff, tmp);
        // 截取KCV
        if(bOK)
        {
            kcv += tmp.SubArray(0, kcvLen);
        }
        return bOK;
    }
    /// 加密数据
    bool _Encrypt(byte mode, const ByteArray& data, ByteBuilder& buff)
    {
        Ref<ISecurityAlgorithm> encAlg;
        ASSERT_FuncErrInfo(Cipcher.Get(mode, encAlg), DeviceError::ArgRangeErr, "加密器不存在");
        ASSERT_FuncErrInfo(!encAlg.IsNull(), DeviceError::ArgIsNullErr, "加密器错误");
        ASSERT_FuncErrInfo(encAlg->Encrypt(data, buff), DeviceError::OperatorErr, "加密数据失败");
        return true;
    }
    /// 解密数据
    bool _Decrypt(byte mode, const ByteArray& data, ByteBuilder& buff)
    {
        Ref<ISecurityAlgorithm> decAlg;
        ASSERT_FuncErrInfo(Cipcher.Get(mode, decAlg), DeviceError::ArgRangeErr, "解密器不存在");
        ASSERT_FuncErrInfo(!decAlg.IsNull(), DeviceError::ArgIsNullErr, "解密器错误");
        ASSERT_FuncErrInfo(decAlg->Decrypt(data, buff), DeviceError::OperatorErr, "解密数据失败");
        return true;
    }
    /// 发送一次数据
    bool _Write(byte mode, const ByteArray& data)
    {
        LOGGER(_log.WriteLine("Send:").WriteLine(data));
        _sendBuff.Clear();
        _cacheBuff.Clear();
        ASSERT_FuncErr(PackCMD(mode, data, _sendBuff), DeviceError::ArgErr);
        LOGGER(_log.WriteLine("Send ENC:").WriteLine(_sendBuff));
        ASSERT_FuncErr(_pDev->Write(_sendBuff), DeviceError::SendErr);
        return true;
    }
    /// 接收一次数据
    bool _Read(byte& mode, ByteBuilder& data)
    {
        _recvBuff.Clear();
        if(!_cacheBuff.IsEmpty())
        {
            _recvBuff.Swap(_cacheBuff);
        }
        LOGGER(if(!_cacheBuff.IsEmpty())
        {
            _log.WriteLine("PrefixCachedCmd:").WriteLine(_cacheBuff);
        });
        if(!RecvByFormat(_pDev, _recvBuff, &_cacheBuff))
        {
            _recvBuff.Clear();
            _cacheBuff.Clear();
            _logErr(DeviceError::RecvErr);
            return false;
        }
        LOGGER(_log.WriteLine("Recv DEC:").WriteLine(_recvBuff));
        LOGGER(if(!_cacheBuff.IsEmpty())
        {
            _log.WriteLine("CachedCmd:").WriteLine(_cacheBuff);
        });
        bool bOK = UnpackCMD(_recvBuff, mode, data);
        LOGGER(if(bOK)
        {
            _log.WriteLine("Recv:").WriteLine(data);
        });
        return bOK;
    }
public:
    LC_EncryptCmdAdapter() : DevAdapterBehavior()
    {
        EncMode = 0x00;
        AckMode = EncMode;
        LrcSize = 0x08;
        _rMode = 0x00;
        Ack.Clear();
    }
    /// 获取/设置当前的传输模式
    byte EncMode;
    /// 获取/设置当前ACK的回应包传输格式
    byte AckMode;
    /// 获取/设置当前ACK的回应包数据
    ByteBuilder Ack;
    /// 获取/设置当前校验包数据长度
    byte LrcSize;
    /// 与模式相对应的密钥器
    MapArguments<byte, Ref<ISecurityAlgorithm> > Cipcher;
    /// 获取上次读取数据时的模式
    inline byte Mode() const
    {
        return _rMode;
    }
    /// 清空接收ACK时提前返回的缓存数据,返回是否有缓存数据
    inline bool Clean()
    {
        bool isCached = !_cacheBuff.IsEmpty();
        _cacheBuff.Clear();
        return isCached;
    }
    /// 组包密文数据
    bool PackCMD(byte mode, const ByteArray& data, ByteBuilder& encBuff)
    {
        const byte ACK_STX = 0x02;
        const byte ACK_ETX = 0x03;
        const size_t LEN_SIZE = 4;

        LOGGER(_log << "EncMode:<" << _hex_num(mode) << ">\n");
        size_t offset = encBuff.GetLength();
        encBuff += ACK_STX;
        encBuff += mode;
        encBuff += LrcSize;
        encBuff.Append(0x00, LEN_SIZE);

        size_t lastLen = encBuff.GetLength();
        ASSERT_Func(_Encrypt(mode, data, encBuff));
        LOGGER(_log.WriteLine("Enc Send:").WriteLine(encBuff.SubArray(offset + 7)));

        size_t len = encBuff.GetLength() - offset - lastLen;
        ByteBuilder lenBuff(4);
        ByteConvert::FromObject(len, lenBuff, LEN_SIZE);
        byte* pBuff = const_cast<byte*>(encBuff.GetBuffer() + offset + 3);
        memcpy(pBuff, lenBuff.GetBuffer(), LEN_SIZE);
        ASSERT_Func(_LRC(mode, data, encBuff, LrcSize));
        LOGGER(_log.WriteLine("Enc LRC:").WriteStream(encBuff.SubArray(7 + len + offset)).WriteLine());
        encBuff += ACK_ETX;
        return true;
    }
    /// 解包密文数据
    bool UnpackCMD(const ByteArray& data, byte& mode, ByteBuilder& decBuff, ByteBuilder* pLrc = NULL)
    {
        const byte ACK_STX = 0x02;
        const size_t LEN_SIZE = 4;
        const size_t CMD_HEAD_LEN = LEN_SIZE + 3;
        ASSERT_FuncErr(data.GetLength() >= CMD_HEAD_LEN, DeviceError::ArgLengthErr);
        ASSERT_FuncErr(data[0] == ACK_STX, DeviceError::ArgFormatErr);
        mode = data[1];

        ByteBuilder lenBuff(4);
        lenBuff = data.SubArray(3, LEN_SIZE);
        size_t len = 0;
        ByteConvert::ToObject<size_t>(lenBuff, len, LEN_SIZE);
        ByteArray buff = data.SubArray(3 + LEN_SIZE, len);
        LOGGER(_log << "DecMode:<" << _hex_num(mode) << ">\n");
        LOGGER(_log.WriteLine("Dec Recv:").WriteLine(buff));
        ASSERT_FuncErrInfo(_Decrypt(mode, buff, decBuff), DeviceError::OperatorErr, "解密数据失败");
        // 比对KCV
        byte lrclen = data[2];
        if(lrclen < 1)
            return true;
        ByteArray kcv = data.SubArray(3 + LEN_SIZE + len, lrclen);
        ByteBuilder lrc(8);
        ASSERT_FuncErrInfo(_LRC(mode, decBuff, lrc, lrclen), DeviceError::OperatorErr, "计算LRC失败");
        if(pLrc != NULL)
        {
            pLrc->Append(lrc);
        }
        LOGGER(_log.WriteLine("Recv LRC:").WriteStream(kcv).WriteLine());
        LOGGER(_log.WriteLine("Dec  LRC:").WriteStream(lrc).WriteLine());
        ASSERT_FuncErrInfo(lrc.IsEqual(kcv), DeviceError::DevReturnErr, "LRC密钥校验失败");
        return true;
    }
    /**
     * @brief 根据当前设定的模式发送密文数据
     * @description 根据EncryptMode在Cipcher中查找加密器,加密器存在则进行加解密操作,不存在报错
     * @date 2017-10-14 09:51
     */
    virtual bool Write(const ByteArray& data)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        ASSERT_FuncRet(_Write(EncMode, data));
        Ack.Clear();
        ASSERT_FuncInfoRet(_Read(_rMode, Ack), "接收ACK失败");
        return _logRetValue(true);
    }
    /// 接收数据 
    virtual bool Read(ByteBuilder& data)
    {
        LOG_FUNC_NAME();
        ASSERT_Device();
        ASSERT_FuncRet(_Read(_rMode, data));
        ASSERT_FuncInfoRet(_Write(AckMode, Ack), "回应ACK失败");
        return _logRetValue(true);
    }
};
//--------------------------------------------------------- 
} // namespace lc
} // namespace application 
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_LC_ENCRYPTCMDADAPTER_H_
//========================================================= 